﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using AlbumArtOrganizer.AlbumScanner;
using AlbumArtOrganizer.ImageFinder;

namespace AlbumArtOrganizer.UI
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    [Export("MainWindow", typeof(Window))]
    public partial class MainWindow
    {
        [ImportMany(typeof(IAlbumScannerFactory))]
        IEnumerable<IAlbumScannerFactory> _albumScannerFactories = null;

        [ImportMany(typeof(IImageFinder))]
        IEnumerable<IImageFinder> _imageFinders = null;

        public static RoutedCommand ImageClickRoutedCommand = new RoutedCommand();

        System.Collections.ObjectModel.ObservableCollection<AlbumArtOrganizer.Model.IAlbum> _albumsWithoutCoverArt;
        System.Collections.ObjectModel.ObservableCollection<AlbumArtOrganizer.Model.IAlbum> _albums;
        CollectionViewSource _collectionViewSourceAlbums;
        CollectionViewSource _collectionViewSourceImagesFound;

        System.Collections.ObjectModel.ObservableCollection<AlbumArtOrganizer.Model.IImage> _imagesFound;

        public MainWindow()
        {
            _albums = new System.Collections.ObjectModel.ObservableCollection<AlbumArtOrganizer.Model.IAlbum>();
            _albumsWithoutCoverArt = new System.Collections.ObjectModel.ObservableCollection<AlbumArtOrganizer.Model.IAlbum>();
            _imagesFound = new System.Collections.ObjectModel.ObservableCollection<AlbumArtOrganizer.Model.IImage>();
            InitializeComponent();
            Loaded += MainWindow_Loaded;

            _collectionViewSourceAlbums = Resources["collectionViewSourceAlbums"] as CollectionViewSource;
            _collectionViewSourceAlbums.Source = _albums;

            _collectionViewSourceImagesFound = Resources["collectionViewSourceImagesFound"] as CollectionViewSource;
            _collectionViewSourceImagesFound.Source = _imagesFound;

            checkboxHideAllWithCoverArt.IsChecked = true;

            var assembly = System.Reflection.Assembly.GetExecutingAssembly();
            var version = assembly.GetName().Version;
            Title = String.Format("Album Art Organizer {0}", version);
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);

            try
            {
                // Load window placement details for previous application session from application settings
                // Note - if window was closed on a monitor that is now disconnected from the computer,
                //        SetWindowPlacement will place the window onto a visible monitor.
                var windowPlacement = Properties.Settings.Default.WindowPlacement;
                windowPlacement.length = Marshal.SizeOf(typeof(Model.WindowPlacement));
                windowPlacement.flags = 0;
                windowPlacement.showCmd = (windowPlacement.showCmd == WindowPlacement.SW_SHOWMINIMIZED ? WindowPlacement.SW_SHOWNORMAL : windowPlacement.showCmd);
                IntPtr hwnd = new WindowInteropHelper(this).Handle;
                WindowPlacement.SetWindowPlacement(hwnd, ref windowPlacement);
                gridcolumndefinitionLeft.Width = new GridLength(Properties.Settings.Default.GridColumnLeftWidth);
            }
            catch
            {
            }
        }

        // WARNING - Not fired when Application.SessionEnding is fired
        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);

            // Persist window placement details to application settings
            Model.WindowPlacement windowPlacement;
            IntPtr hwnd = new WindowInteropHelper(this).Handle;
            WindowPlacement.GetWindowPlacement(hwnd, out windowPlacement);
            Properties.Settings.Default.WindowPlacement = windowPlacement;
            Properties.Settings.Default.GridColumnLeftWidth = (int)gridcolumndefinitionLeft.Width.Value;
            Properties.Settings.Default.SelectedAlbumScannerPlugin = comboboxAlbumScanner.Text;
            Properties.Settings.Default.Save();
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            comboboxAlbumScanner.ItemsSource = _albumScannerFactories;
            comboboxAlbumScanner.Text = Properties.Settings.Default.SelectedAlbumScannerPlugin;
            listboxImageFinderOptionGroups.ItemsSource = _imageFinders;
        }

        private void ButtonSearchClick(object sender, RoutedEventArgs e)
        {
            var button = sender as Button;
            if (button != null)
            {
                var previousAlbum = button.TryFindResource("previousAlbum") as AlbumArtOrganizer.Model.IAlbum;
                var imageFinder = button.DataContext as IImageFinder;
                var album = contentcontrol.Content as AlbumArtOrganizer.Model.IAlbum;
                // Are we looking for a new album or for an "old" one? This could possibly be done at selection-time too, 
                // but then we would lose the ability to apply the same image to several different album entries 
                // without having to search once (or even several times) for each album entry.
                if (album != previousAlbum)
                {
                    _imagesFound.Clear();
                }
                if (album != null && imageFinder != null)
                {
                    textblockAlbumScannerStatus.Text = String.Format("Initiating {0} search for cover image for {1} - {2}...", imageFinder.Name, album.Artist.Name, album.Title);
                    ShowProgressBars();
                    var backgroundWorkerImageFinder = new BackgroundWorker();
                    backgroundWorkerImageFinder.DoWork += BackgroundWorkerImageFinderDoWork;
                    backgroundWorkerImageFinder.ProgressChanged += BackgroundWorkerImageFinderProgressChanged;
                    backgroundWorkerImageFinder.RunWorkerCompleted += BackgroundWorkerImageFinderRunWorkerCompleted;
                    backgroundWorkerImageFinder.WorkerReportsProgress = true;
                    backgroundWorkerImageFinder.RunWorkerAsync(Tuple.Create(imageFinder, album));
                    button.Resources["previousAlbum"] = album;
                }
            }
        }

        void BackgroundWorkerImageFinderRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            HideProgressBars();
            var tuple = e.Result as Tuple<IImageFinder, AlbumArtOrganizer.Model.IAlbum>;
            if (tuple == null)
            {
                return;
            }
            var imageFinder = tuple.Item1;
            var album = tuple.Item2;
            textblockAlbumScannerStatus.Text = String.Format("{0} image search complete for {1} - {2}.", imageFinder.Name, album.Artist.Name, album.Title);
        }

        void BackgroundWorkerImageFinderProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            var tuple = e.UserState as Tuple<IImageFinder, AlbumArtOrganizer.Model.IAlbum>;
            if (tuple == null)
            {
                return;
            }
            var imageFinder = tuple.Item1;
            var album = tuple.Item2;
            if (imageFinder != null && album != null)
            {
                textblockAlbumScannerStatus.Text = String.Format("Searching for cover image for {1} - {2} on {0}...", imageFinder.Name, album.Artist.Name, album.Title);
            }
        }

        void BackgroundWorkerImageFinderDoWork(object sender, DoWorkEventArgs e)
        {
            var backgroundWorker = sender as BackgroundWorker;
            var tuple = e.Argument as Tuple<IImageFinder, AlbumArtOrganizer.Model.IAlbum>;
            if (backgroundWorker == null || tuple == null)
            {
                return;
            }
            var imageFinder = tuple.Item1;
            var album = tuple.Item2;
            backgroundWorker.ReportProgress(1, tuple);
            if (imageFinder != null && album != null)
            {
                try
                {
                    var imageFinderResults = imageFinder.FindImages(album.Artist.Name, album.Title);
                    var actionUpdateImages = new Action<IImageFinderResults>(ifr =>
                    {
                        foreach (var image in ifr.Images)
                        {
                            _imagesFound.Add(image);
                        }
                    });
                    Dispatcher.Invoke(actionUpdateImages, imageFinderResults);
                    e.Result = tuple;
                }
                catch(Exception exception)
                {
                    Dispatcher.Invoke(new Action(() => MessageBox.Show(exception.Message)));
                }
                finally
                {
                    backgroundWorker.ReportProgress(100, tuple);
                }
            }
        }

        private void ButtonAlbumScannerClick(object sender, RoutedEventArgs e)
        {
            var albumScannerFactory = comboboxAlbumScanner.SelectedItem as IAlbumScannerFactory;
            _albums.Clear();
            textblockAlbumScannerStatus.Text = "Initiating album scanner...";
            ShowProgressBars();
            var backgroundWorkerAlbumScanner = new BackgroundWorker();
            backgroundWorkerAlbumScanner.DoWork += BackgroundWorkerAlbumScannerDoWork;
            backgroundWorkerAlbumScanner.ProgressChanged += BackgroundWorkerAlbumScannerProgressChanged;
            backgroundWorkerAlbumScanner.RunWorkerCompleted += BackgroundWorkerAlbumScannerRunWorkerCompleted;
            backgroundWorkerAlbumScanner.WorkerReportsProgress = true;
            backgroundWorkerAlbumScanner.RunWorkerAsync(albumScannerFactory);
        }

        void BackgroundWorkerAlbumScannerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            textblockAlbumScannerStatus.Text = String.Format("Album scan complete. Found {0} albums.", _albums.Count);
            HideProgressBars();
        }

        void BackgroundWorkerAlbumScannerProgressChanged(object sender, ProgressChangedEventArgs e)
        {
        }

        void BackgroundWorkerAlbumScannerDoWork(object sender, DoWorkEventArgs e)
        {
            var backgroundWorker = sender as BackgroundWorker;
            var albumScannerFactory = e.Argument as IAlbumScannerFactory;
            if (backgroundWorker == null || albumScannerFactory == null)
            {
                return;
            }
            var albumScanner = albumScannerFactory.CreateAlbumScanner(albumScannerFactory.Options);
            albumScanner.ReportProgress += (reportProgressSender, reportProgressE) =>
            {
                var action = new Action<AlbumScannerReportProgressEventArgs>(progress =>
                {
                    var percentageComplete = String.Empty;
                    if (reportProgressE.PercentageComplete.HasValue)
                    {
                        percentageComplete = String.Format(" {0}% complete",
                                                           Math.Round(reportProgressE.PercentageComplete.Value*100, 2));
                    }
                    string statusText;
                    if (progress.Album != null)
                    {
                        var album = progress.Album;
                        statusText = String.Format("Found {0} - {1}...{2}",
                                                   album.Artist.Name,
                                                   album.Title,
                                                   percentageComplete);
                        _albums.Add(album);
                        if (!album.CoverImage.Exists)
                        {
                            _albumsWithoutCoverArt.Add(album);
                        }
                    }
                    else
                    {
                        statusText = String.Format("Scanning Windows Media Player library...{0}",
                                                   percentageComplete);
                    }
                    textblockAlbumScannerStatus.Text = statusText;
                });
                Dispatcher.BeginInvoke(action, reportProgressE);
                backgroundWorker.ReportProgress(1);
            };
            albumScanner.Completed += (completedSender, completedE) => backgroundWorker.ReportProgress(100);
            albumScanner.GetAlbums();
        }

        private void HyperlinkSaveImageClick(object sender, RoutedEventArgs e)
        {
            var link = sender as Hyperlink;
            if (link == null)
            {
                return;
            }
            var albumSave = contentcontrol.Content as AlbumArtOrganizer.Model.IAlbum;
            var imageSave = link.DataContext as AlbumArtOrganizer.Model.IImage;
            if (imageSave != null && albumSave != null)
            {
                ShowProgressBars();
                var albumScannerFactory = comboboxAlbumScanner.SelectedItem as IAlbumScannerFactory;
                _albums.Remove(albumSave);
                _albumsWithoutCoverArt.Remove(albumSave);
                textblockAlbumScannerStatus.Text = String.Format("Initiating image save for album {0} - {1}...", albumSave.Artist.Name, albumSave.Title);
                var backgroundWorkerImageSaver = new BackgroundWorker();
                backgroundWorkerImageSaver.DoWork += BackgroundWorkerImageSaverDoWork;
                backgroundWorkerImageSaver.ProgressChanged += BackgroundWorkerImageSaverProgressChanged;
                backgroundWorkerImageSaver.RunWorkerCompleted += BackgroundWorkerImageSaverRunWorkerCompleted;
                backgroundWorkerImageSaver.WorkerReportsProgress = true;
                backgroundWorkerImageSaver.RunWorkerAsync(Tuple.Create(albumScannerFactory, albumSave, imageSave));
            }
        }

        void BackgroundWorkerImageSaverRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                textblockAlbumScannerStatus.Text = String.Format("An error occurred while saving image: '{0}'", e.Error.Message);
            }
            var albumSave = e.Result as AlbumArtOrganizer.Model.IAlbum;
            if (albumSave != null)
            {
                _albums.Add(albumSave);
                contentcontrolCover.Content = null;
                contentcontrolCover.Content = albumSave;
                textblockAlbumScannerStatus.Text = String.Format("Saved image for album {0} - {1}.", albumSave.Artist.Name, albumSave.Title);
            }
            HideProgressBars();
        }

        void BackgroundWorkerImageSaverProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            var albumSave = e.UserState as AlbumArtOrganizer.Model.IAlbum;
            if (albumSave != null)
            {
                textblockAlbumScannerStatus.Text = String.Format("Saving image for album {0} - {1}...", albumSave.Artist.Name, albumSave.Title);
            }
        }

        static void BackgroundWorkerImageSaverDoWork(object sender, DoWorkEventArgs e)
        {
            var tuple = e.Argument as Tuple<IAlbumScannerFactory, AlbumArtOrganizer.Model.IAlbum, AlbumArtOrganizer.Model.IImage>;
            var backgroundWorker = sender as BackgroundWorker;
            if (tuple == null || backgroundWorker == null)
            {
                return;
            }
            var albumScannerFactory = tuple.Item1;
            var albumSave = tuple.Item2;
            var imageSave = tuple.Item3;
            backgroundWorker.ReportProgress(1, albumSave);
            if (imageSave == null || albumSave == null || albumScannerFactory == null)
            {
                return;
            }
            var albumScanner = albumScannerFactory.CreateAlbumScanner(albumScannerFactory.Options);
            albumScanner.ReplaceCoverImage(albumSave, imageSave);
            e.Result = albumSave;
            backgroundWorker.ReportProgress(100);
        }

        private void HyperlinkOpenInBrowserClick(object sender, RoutedEventArgs e)
        {
            var link = sender as Hyperlink;
            if (link == null)
            {
                return;
            }
            var image = link.DataContext as AlbumArtOrganizer.Model.IImage;
            if (image != null)
            {
                System.Diagnostics.Process.Start(image.FullSizeLocation.AbsoluteUri);
            }
        }

        FilterEventHandler _collectionViewSourceAlbumsCoverArtFilter;
        FilterEventHandler CollectionViewSourceAlbumsCoverArtFilter
        {
            get
            {
                if (_collectionViewSourceAlbumsCoverArtFilter == null)
                {
                    _collectionViewSourceAlbumsCoverArtFilter = new FilterEventHandler(collectionViewSourceAlbums_CoverArtFilter);
                }
                return _collectionViewSourceAlbumsCoverArtFilter;
            }
        }

        private void collectionViewSourceAlbums_CoverArtFilter(object sender, FilterEventArgs e)
        {
            var album = e.Item as AlbumArtOrganizer.Model.IAlbum;
            if (album != null)
            {
                e.Accepted = !album.CoverImage.Exists;
            }
        }

        private void CheckboxHideAllWithCoverArtChecked(object sender, RoutedEventArgs e)
        {
            _collectionViewSourceAlbums.Filter += CollectionViewSourceAlbumsCoverArtFilter;
        }

        private void CheckboxHideAllWithCoverArtUnchecked(object sender, RoutedEventArgs e)
        {
            _collectionViewSourceAlbums.Filter -= CollectionViewSourceAlbumsCoverArtFilter;
        }

        private void ButtonOptionDirectoryClick(object sender, RoutedEventArgs e)
        {
            var button = sender as Button;
            if (button == null)
            {
                return;
            }
            var optionDirectory = button.DataContext as AlbumArtOrganizer.Model.OptionDirectory;
            if (optionDirectory != null)
            {
                var folderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog
                {
                    SelectedPath = optionDirectory.Value,
                    ShowNewFolderButton = false
                };
                var dialogResult = folderBrowserDialog.ShowDialog();
                if (dialogResult == System.Windows.Forms.DialogResult.OK)
                {
                    optionDirectory.Value = folderBrowserDialog.SelectedPath;
                }
            }
        }

        private void ButtonLinkAlbumClick(object sender, RoutedEventArgs e)
        {
            var button = sender as Button;
            if (button == null)
            {
                return;
            }
            var album = button.DataContext as AlbumArtOrganizer.Model.IAlbum;
            if (album != null)
            {
                contentcontrolCover.Content = album;
                contentcontrol.Content = album;
                foreach (var imageFinder in _imageFinders)
                {
                    imageFinder.CurrentAlbum = album;
                }
            }
        }

        private void ImageCoverClick(object sender, ExecutedRoutedEventArgs e)
        {
            var image = sender as Image;
            if (image == null)
            {
                return;
            }
            if (image.DataContext is AlbumArtOrganizer.Model.IAlbum)
            {
                var album = image.DataContext as AlbumArtOrganizer.Model.IAlbum;
                ShowOverlayImage(album.CoverImage.Image);
            }
            else if (image.DataContext is AlbumArtOrganizer.Model.IImage)
            {
                var imageInfo = image.DataContext as AlbumArtOrganizer.Model.IImage;
                ShowOverlayImage(imageInfo.FullSizeImage);
            }
        }

        private void ImageCoverCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            var image = sender as Image;
            if (image == null)
            {
            }
            else
            {
                var bitmapSource = image.Source as BitmapSource;
                if (bitmapSource != null)
                {
                    e.CanExecute = true;
                }
                else
                {
                    e.CanExecute = false;
                }
            }
            e.Handled = true;
        }

        private void ShowOverlayImage(BitmapImage image)
        {
            borderOverlay.Visibility = Visibility.Visible;
            imageOverlay.Source = image;
            imageOverlay.MaxWidth = 800;
            imageOverlay.MaxHeight = 800;
            var blurEffect = new System.Windows.Media.Effects.BlurEffect
            {
                Radius = 10
            };
            dockpanelMain.Effect = blurEffect;
        }

        void ShowProgressBars()
        {
            progressbarAlbumScanner.IsIndeterminate = true;
            progressbarAlbumScanner.Value = 1.0;
            progressbarAlbumScanner.Opacity = 1.0;
            TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Indeterminate;
        }

        private void HideProgressBars()
        {
            progressbarAlbumScanner.IsIndeterminate = false;
            progressbarAlbumScanner.Value = 0.0;
            progressbarAlbumScanner.Opacity = 0.0;
            TaskbarItemInfo.ProgressValue = 1.0;
            TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None;
        }

        private void ButtonCloseOverlayClick(object sender, RoutedEventArgs e)
        {
            dockpanelMain.Effect = null;
            borderOverlay.Visibility = Visibility.Collapsed;
            imageOverlay.Source = null;
        }


    }

    public class ArtistViewModel
    {
        public ArtistViewModel()
        {
            Albums = new System.Collections.ObjectModel.ObservableCollection<AlbumArtOrganizer.Model.IAlbum>();
        }

        public AlbumArtOrganizer.Model.IArtist Artist
        {
            get;
            set;
        }

        public System.Collections.ObjectModel.ObservableCollection<AlbumArtOrganizer.Model.IAlbum> Albums
        {
            get;
            set;
        }
    }
}
